package vsqlbuilder.gui.diagram;

import static vsqlbuilder.gui.diagram.DiagramModel.FIELD_IMAGE_ICON;
import static vsqlbuilder.gui.diagram.DiagramModel.FONT;
import static vsqlbuilder.gui.diagram.DiagramModel.NNFIELD_IMAGE_ICON;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;

import vsqlbuilder.logic.Column;
import vsqlbuilder.logic.Table;

public class GTable implements IGshape {

	// Main Draw Stroke
	private static final Stroke SELECTION_STROKE = new BasicStroke(1.0f,
			BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {
					3.0f, 3.0f }, 0.0f);

	// Indicate if this table is selected by user or not.
	public boolean selected;

	// Table Logic Object
	private Table table;

	// Rectangle, use it as table border
	private Rectangle tableBorder;

	// Colors
	private static final Color BORDER_COLOR1 = new Color(172, 168, 153);
	private static final Color BORDER_COLOR2 = new Color(112, 112, 112);
	private static final Color BORDER_COLOR3 = new Color(208, 208, 208);
	private static final Color TITLE_BACKGROUND_COLOR = new Color(245, 245, 245);
	private static final int MIN_TABLE_WIDTH = 80;
	private static final int MIN_TABLE_HEIGHT = 54;
	private static final int ROW_HEIGHT = 17;
	private static final int IMAGE_HEIGHT = 16;
	private static final int IMAGE_WIDTH = 16;
	private static final int TEXT_MARGIN = 3;
	private static final int IMAGE_LEFT_MARGIN = 1;
	private static final int IMAGE_TOP_MARGIN = 1;
	private final FontMetrics FONT_METRICS;
	
	private List<GAnchorPoint> AnchorPoints;

	// Functions Declarations
	/*
	 * Constructor of this class. x,y indicate coordinates of creation of the
	 * object in the drawing surface. Name indicate table name that will be
	 * display as table title.
	 */
	public GTable(int x, int y, Table t, FontMetrics fm) {
		this.tableBorder = new Rectangle();
		this.tableBorder.x = x;
		this.tableBorder.y = y;
		this.tableBorder.width = MIN_TABLE_WIDTH;
		this.tableBorder.height = MIN_TABLE_HEIGHT;
		this.table = t;
		this.selected = false;
		this.FONT_METRICS = fm;
		computeWidthHeight();
		AnchorPoints = new ArrayList<GAnchorPoint>();
		createAnchors();
	}

	public List<GAnchorPoint> getAnchorPointsList(){
		return this.AnchorPoints;
	}
	
	public void createAnchors(){
		int ColsCount = this.table.getColsNumber();
		for (int i = 0; i < ColsCount; i++) {
			Column column = this.table.getColumn(i);
			AnchorPoints.add(new GAnchorPoint(this,column));
		}
	}
	
	public GAnchorPoint getAnchorPoint(Column c){
		GAnchorPoint temp=null;
		for (GAnchorPoint ap : AnchorPoints){
			if(ap.getColumnName().equalsIgnoreCase(c.getName())){
				temp=ap;
			}
		}
		return temp;
	}
	
	public boolean contains(int x, int y) {
		return this.tableBorder.contains(x, y);
	}

	public void move(int x1, int y1, int x2, int y2) {
		// TODO [low]: Research another way of doing this moving function if
		// possible (Double Buffer).
		this.tableBorder.setLocation(x2 - x1, y2 - y1);
		// [BUG fix #3] x1 and y1 are not axis coordinates, they are distances
		// from the left top point of table to the click point for moving the
		// table
	}

	public void paint(Graphics2D g) {
		// Draw Main Table Shape including name
		Font FONTnormal = new Font("Dialog", Font.PLAIN, 12);
		Font FONTselected = new Font("Dialog", Font.BOLD, 12);
		Color currentColor = g.getColor();
		Font currentFont = g.getFont();
		g.setFont(FONT);
		g.draw(this.tableBorder);
		g.setColor(BORDER_COLOR3);
		g.drawRect(this.tableBorder.x, this.tableBorder.y,
				this.tableBorder.width + 2, this.tableBorder.height + 2);

		g.setColor(BORDER_COLOR2);
		g.drawRect(this.tableBorder.x, this.tableBorder.y,
				this.tableBorder.width + 1, this.tableBorder.height + 1);

		g.setColor(Color.WHITE);
		g.drawRect(this.tableBorder.x, this.tableBorder.y,
				this.tableBorder.width, this.tableBorder.height);

		int y = this.tableBorder.y;
		int height = ROW_HEIGHT + 1;
		g.setColor(TITLE_BACKGROUND_COLOR);
		g.fillRect(this.tableBorder.x, y, this.tableBorder.width, height);

		g.setColor(Color.BLACK);
		g.drawString(this.table.getName(), this.tableBorder.x + TEXT_MARGIN, y
				+ ROW_HEIGHT - 3);
		y += height;

		// Draw Columns inside table
		int ColsCount = this.table.getColsNumber();
		height = ROW_HEIGHT * (ColsCount) + 1;
		if (height > 1) { // if there are columns
			g.setColor(BORDER_COLOR1);
			g.drawLine(this.tableBorder.x, y, this.tableBorder.x
					+ this.tableBorder.width, y);
			for (int i = 0; i < ColsCount; i++) {
				Column column = this.table.getColumn(i);
				height = ROW_HEIGHT * (i + 1);
				// TODO [High] if (column is primary key then draw icon of
				// primary key
				// )
				// Draw image of field
				if(column.isNotNull()){
				g.drawImage(NNFIELD_IMAGE_ICON.getImage(), this.tableBorder.x
						+ IMAGE_LEFT_MARGIN, y + height - IMAGE_HEIGHT
						+ IMAGE_TOP_MARGIN, null);
				}else{
					g.drawImage(FIELD_IMAGE_ICON.getImage(), this.tableBorder.x
							+ IMAGE_LEFT_MARGIN, y + height - IMAGE_HEIGHT
							+ IMAGE_TOP_MARGIN, null);					
				}

				// Draw Name of Column depending of selected or not for query
				if (column.getQuerySelected()) {
					g.setFont(FONTselected);
					g.setColor(Color.BLACK);
				} else {
					g.setFont(FONTnormal);
					g.setColor(Color.LIGHT_GRAY);
				}
				g.drawString(column.toString(), this.tableBorder.x
						+ TEXT_MARGIN + IMAGE_WIDTH, y + height - TEXT_MARGIN);
			}
			y += height + 1;
		}

		// Change table border when selected
		if (selected) {

			Stroke currentStroke = g.getStroke();

			g.setColor(Color.WHITE);
			g.drawRect(this.tableBorder.x, this.tableBorder.y,
					this.tableBorder.width, this.tableBorder.height);

			g.setColor(Color.BLACK);
			g.setStroke(SELECTION_STROKE);
			g.drawRect(this.tableBorder.x, this.tableBorder.y,
					this.tableBorder.width, this.tableBorder.height);

			g.setStroke(currentStroke);
		} else {
			g.setColor(BORDER_COLOR1);
			g.drawRect(this.tableBorder.x, this.tableBorder.y,
					this.tableBorder.width, this.tableBorder.height);
		}

		// return values to default
		g.setColor(currentColor);
		g.setFont(currentFont);
	}

	public Rectangle getRectangle() {
		return this.tableBorder;
	}

	// setter of Selected property of class to indicate if table is selected or
	// not
	public void setSelected(boolean value) {
		this.selected = value;
	}

	// getter of Selected property of class to indicate if table is selected or
	// not
	public boolean getSelected() {
		return this.selected;
	}

	public Table getShapeLogic() {
		return this.table;
	}

	// Get the Column at the x,y position of the figure (this position is
	// relative to rectangle not absolute x,y)
	public Column getColumnAt(int x, int y) {
		int position = -1;
		// TODO [High]: implements the X axis use to limit the getColumn to the
		// text of
		// column name or a fixed size (Horizontally)
		if (this.table.getColsNumber() > 0) {
			position = y / ROW_HEIGHT;
			position--; // [BUG fix #2] position fixed, because first is 0 not
			// 1;
		}
		if (position >= 0)
			return this.table.getColumn(position);
		else
			return null;
	}

	//Get the position of the Join connector at column (c) in a table, relative to the reference point if avaible
	public Point getJoinAtColPos(Column c, Point reference){
		Point p = new Point();
		int ColsCount = this.table.getColsNumber();
		int height=0;
		
		//Calculate y position in table
		for (int i = 0; i < ColsCount; i++) {
			Column column = this.table.getColumn(i);
			if(c.getName().equalsIgnoreCase(column.getName())){
				height = ROW_HEIGHT * (i + 2);
				height-= ROW_HEIGHT/2;
			}
		 }
		
		//Calculate x position in table
		int x=(this.getWidth()/2)+this.tableBorder.x;  //Default value at middle of table
		if(reference!=null){
			//The  join link must be at right or left?
			if(x<=reference.x){
				x=this.tableBorder.x+this.getWidth();
			}else{
				x=this.tableBorder.x;
			}
		}

		//Adjust y position in diagram
		p.y=this.tableBorder.y+height;
		p.x=x;
		return p;
		}
	
	private void computeWidthHeight() {

		// Computing width of table graph
		// Starts with the length of 0
		int maxWidth = 0;
		int width = FONT_METRICS.stringWidth(this.table.getName());
		if (width > maxWidth)
			maxWidth = width;
		// Look in all columns to probe if there is a width larger than actual
		// value of maxWidth
		int count = this.table.getColsNumber();
		for (int i = 0; i < count; i++) {
			Column column = this.table.getColumn(i);
			width = IMAGE_WIDTH + FONT_METRICS.stringWidth(column.toString());
			if (width > maxWidth) {
				maxWidth = width;
			}
		}
		// leave a little space of width
		maxWidth += 8;
		// set width between max: default or calculate
		this.tableBorder.width = Math.max(maxWidth, MIN_TABLE_WIDTH);

		// ---------------------Computing width of table graph height
		int height = 1 + ROW_HEIGHT;
		// put a value for height for every column of table [a row in table
		// graph]
		count = this.table.getColsNumber();
		if (count > 0) {
			height += count * ROW_HEIGHT + 1;
		}
		this.tableBorder.height = Math.max(height, MIN_TABLE_HEIGHT);
	}
	
	public int getWidth(){
		return this.tableBorder.width;
	}
	
	public int getHeight(){
		return this.tableBorder.height;
	}

}
